package com.bolt.support.desensitization.resolver;

import com.bolt.common.reflect.ClassUtil;
import com.bolt.common.reflect.ReflectionUtil;
import com.bolt.convention.data.DefaultPageInfo;
import com.bolt.convention.data.PageInfo;
import com.bolt.convention.data.ResultMessage;
import com.bolt.support.desensitization.annotation.DynamicSensitive;
import com.bolt.support.desensitization.annotation.IgnoreSensitive;
import com.bolt.support.desensitization.desensitizer.Desensitizer;
import com.bolt.support.desensitization.exception.DesensitizationException;
import com.bolt.support.desensitization.support.DynamicSensitiveConfigProvider;
import com.bolt.support.desensitization.support.InstanceCreators;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

public class DynamicTypeResolver implements TypeResolver<Object, AnnotatedParameterizedType> {

    private static final ConcurrentMap<Class<Desensitizer<Object, Annotation>>, Desensitizer<Object, Annotation>> DESENSITIZER_CACHE = new ConcurrentHashMap<>();

    private static final ConcurrentMap<Class<? extends Annotation>, Method> DESENSITIZER_METHOD_CACHE = new ConcurrentHashMap<>();


    private final DynamicSensitiveConfigProvider configProvider;

    public DynamicTypeResolver(DynamicSensitiveConfigProvider configProvider) {
        this.configProvider = configProvider;
    }


    @Override
    public Object resolve(Object value, AnnotatedParameterizedType annotatedType) {

        if (value instanceof ResultMessage) {
            ResultMessage resultMessage = (ResultMessage) value;
            Object data = resultMessage.getData();
            if (data instanceof PageInfo) {
                PageInfo pageInfo = ((PageInfo) data);
                Collection content = pageInfo.getContent();
                Collection erased = resolve(content);
                return resultMessage.setData(new DefaultPageInfo(pageInfo.getPageNumber(), pageInfo.getPageSize(), (List) erased, pageInfo.getTotalCount()));
            } else {
                return resultMessage.setData(resolve(data));
            }
        } else {
            return resolve(value);
        }
    }

    @Override
    public boolean support(Object value, AnnotatedType annotatedType) {
        return (value instanceof ResultMessage &&
                annotatedType instanceof AnnotatedParameterizedType &&
                ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments()[0].isAnnotationPresent(DynamicSensitive.class))
                || (value != null && annotatedType.isAnnotationPresent(DynamicSensitive.class));

    }


    @Override
    public int order() {
        return HIGHEST_PRIORITY;
    }

    private Object resolve(Object value) {
        Class<?> clazz = value.getClass();
        Object newObject = InstanceCreators.getInstanceCreator(clazz).create();
        Map<String, Annotation> annotationMap = configProvider.get(clazz.getName());
        ClassUtil.getAllFields(clazz).parallelStream().forEach(field -> {
            Object fieldValue = ReflectionUtil.getFieldValue(value, field);
            Annotation annotation = annotationMap.get(field.getName());
            if (annotation != null && annotation.annotationType().isAnnotationPresent(IgnoreSensitive.class)) {
                return;
            }
            if (!Modifier.isFinal(field.getModifiers()) && (fieldValue != null && annotation != null)) {
                ReflectionUtil.setFieldValue(newObject, field, getDesensitizer(annotation).desensitize(fieldValue, annotation));
            } else {
                ReflectionUtil.setFieldValue(newObject, field, fieldValue);
            }
        });
        return newObject;
    }

    private Collection resolve(Collection<Object> value) {
        return value.parallelStream().map(o -> resolve(o))
                .collect(Collectors.collectingAndThen(Collectors.toList(), erased -> {
                    Collection<Object> collection = InstanceCreators.getInstanceCreator(ClassUtil.getClass(value)).create();
                    collection.addAll(erased);
                    return collection;
                }));
    }

    private Desensitizer<Object, Annotation> getDesensitizer(Annotation annotation) {
        try {
            Method desensitizerMethod = DESENSITIZER_METHOD_CACHE.computeIfAbsent(annotation.annotationType(), annotationClass -> ReflectionUtil.getAccessibleMethod(annotationClass, "desensitizer"));
            return DESENSITIZER_CACHE.computeIfAbsent(ReflectionUtil.invokeMethod(annotation, desensitizerMethod), clazz -> InstanceCreators.getInstanceCreator(clazz).create());
        } catch (Exception e) {
            throw new DesensitizationException(String.format("实例化敏感注解%s的脱敏器失败。", annotation.annotationType()), e);
        }
    }
}